<?php

use Illuminate\Database\Capsule\Manager as DB;

class TestTikuCgz01Model extends BaseModel
{
    /*
      1. 判断是否有 optionsTable ，如果有则为选择题，返回第一个 optionsTable 的位置。
      如果没有optionsTable，判断是否有A. 或全角 “Ａ.” 或“Ａ．”
      如果没有A. 判断 是否有“A”或全角  “Ａ”
      如果没有：检查答案
      如果答案表示是选择题，可能选项是图片，不处理。

      单个选择题处理：
      1. 从上一步定位的开始位置逐个查找 A B C D …… H
      2. 截取各个选项
      3. 转为json存储 p_option，设置 p_option_ok =1 , p_question_type=1 (根据答案的数量，1单选，2多选）

      复合选择题处理：（如果有多个optionsTable 则认为是复合题）
      1. 不处理

      <table name=\"optionsTable\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"><tr><td width=\"100%\">A．初中的学习真难，我恐怕学不好</td></tr><tr><td width=\"100%\">B．初中的学习任务重了，我必须放弃所有的兴趣爱好</td></tr><tr><td width=\"100%\">C．学习科目多了，我必须分清主次，只学好主科</td></tr><tr><td width=\"100%\">D．学习难度大，科目多，我会更加努力</td></tr></table>【小题2】面对这些新的变化，我们应采取的对策是<table name=\"optionsTable\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\"><tr><td width=\"50%\">A．制定学习计划，探索好的学习方法</td><td width=\"50%\">B．减少娱乐和休息</td></tr><tr><td width=\"50%\">C．尽量少参加一些集体活动</td><td width=\"50%\">D．与老师、同学搞好关系</td></tr></table>

      optionsTable 表格格式处理：
      找到 optionsTable  的位置 firstIndex
      从firstIndex位置开始逐个找 A B C D …… H 的位置 optionCharIndex
      找到后截取optionCharIndex 到</td> 之间的内容  如：<td width=\"100%\">A．初中的学习真难，我恐怕学不好</td>
      去除选项后面的“.”
      找到选项内容，继续下一个。

      2011年5月3日，中共中央政治局常委、国务院总理温家宝同20位来自首都各界的青年代表在北京中南海座谈，共话青年成才之路。他对青年成才提出“<u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</u>”五点希望（&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;）<br />A有理想&nbsp;&nbsp;&nbsp; B有纪律&nbsp;&nbsp;&nbsp;&nbsp;C讲道德&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D善学习 &nbsp;&nbsp;&nbsp;&nbsp;E要自立 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F勇奋斗

      直接A B C D 无表格格式处理：
      从起始位置开始逐个找 A B C D …… H 的位置 optionCharIndex
      找到后截取 optionCharIndex 到下一个选项字符之间的内容，如A，就找B ，B就找C
      去除选项后的“.” 以及内容中的“&nbsp;”和“<br />”
      找到选项内容，继续下一个。
     */

    const TABLE_QT = 'tiku.rp_question';
    const TYPE = [
        'danxuan'=>1,
        'duoxuan'=>2,
        'panduan'=>3,
        'jieda'=>4,
        'tiankong'=>5,
        'yuedulijie'=>6,
        'wanxingtiankong'=>7,
    ];

    /**
     * 解析选择题题干中的选项，及格式化答案
     * @throws Exception
     */
    public function paseData()
    {
        $limit = 1000;

        $where = "";
        " where p_question_type=0 ";
        $countsql = "select count(*) as count from " . self::TABLE_QT . "{$where}";

        $count = DB::select($countsql)[0]->count;

        $for_count = ceil($count / $limit);

        echo "待处理数据{$count}每次处理{$limit}条,共需" . $for_count . "次处理完毕\n";

        for ($i = 0; $i < $for_count; $i++) {
            echo "第" . ($i + 1) . "次循环开始\n";

            $limit_str = " limit " . $i * $limit . "," . $limit;
            //TODO 获取题目数据，此处要做成分批处理
            $sql = 'SELECT id, content, answerContent FROM ' . self::TABLE_QT . "{$where}" . '  order by id' . $limit_str;
            $dbData = DB::select($sql);

            $optionSuccessNum = 0;
            $answerSuccessNum = 0;
            foreach ($dbData as $rec) {
                $id = $rec->id;
                $content = $rec->content;
                $answerContent = $rec->answerContent;

                if (is_null($content) || !is_string($content)) {
                    throw new Exception($id . '|题干必须是字符串');
                }
                //进行全角转换
                $contentSbc = $this->convertStrType($content);
                $answerSbc = $this->convertStrType($answerContent);


                //提取答案
                $answer = $this->handlerAnswer($answerSbc);
                $saveData = [];

                if ($answer == '') {
                    $saveData['p_answer_ok'] = 2;
                    $saveData['p_question_type'] = TYPE['jieda'];
                } else {
                    $answerSuccessNum++;
                    $saveData['p_answer_ok'] = 1;
                    $saveData['right_answer'] = $answer;

                    $type = TYPE['danxuan'];
                    if (mb_strlen($answer) > 1) {
                        $type = TYPE['duoxuan'];
                    }
                    $saveData['p_question_type'] = $type;
                }


                //提取选项
                $options = $this->handlerOption($contentSbc, $answer);

                if (count($options) > 0) {
                    //去除题干中的选项
                    $saveData['question_trunk'] = $this->handlerContent($contentSbc);
                    $optionSuccessNum++;
                    $saveData['p_option_ok'] = 1;
                    $saveData['question_option'] = json_encode($options, JSON_UNESCAPED_UNICODE);
                } else {
                    //根据答案猜测题型
                    $saveData['p_question_type'] = $this->guestQuestionType($answerSbc);
                    $saveData['p_option_ok'] = 2;
                }
                //保存数据
                DB::table(self::TABLE_QT)->where(['id' => $id])->update($saveData);
            }

            $total = count($dbData);
            $opFail = $total - $optionSuccessNum;

            echo "一共 $total 条，识别选择题 $optionSuccessNum 条，非选择题 $opFail 条。\n";
        }
    }

    //

    /**
     * 根据答案猜测题型 p_question_type   题型：1 单选、2多选、3复合选择题  (小题)、4判断题 (√× 对错正确错误不对)、5解答题
     * @param $text  答案原始文本
     * @return int
     */
    private function guestQuestionType($text)
    {
        //默认返回解答题
        $qtype = TYPE['jieda'];
        if (strstr($text, "小题")) {
            $qtype = TYPE['yuedulijie'];
        } else {
            preg_match_all('/(√|×|对|错|正确|错误|不对)/', $text, $matches);
            if (count($matches[0]) == 1) {
                $qtype = 4;
            }
        }
        return $qtype;
    }

    /**
     * 从题干中去除选择题选项
     * @param $content
     * @return array|null|string|string[]
     */
    private function handlerContent($content)
    {

        $choicePos = $this->isChoice($content);
        if ($choicePos === false) {
            return [];
        }

        if ($choicePos['reg'] === 'optionsTable') {
            return preg_replace('/<table name="optionsTable".*/', '', $content);
        } else {
            $matchNum = preg_match_all("/[<br>]A(.*?)<\//is",$content,$pregs);
            if($matchNum>0) {
                return $pregs[1][0];
            }else $content;
        }
    }

    /**
     * 格式化选择题和判断题的答案
     * @param  string $text 原始字符串
     * @return string 格式化后的答案
     */
    private function handlerAnswer($text)
    {
        if (strstr($text, "小题") || preg_match("/[1-7]\.[A-H]/", $text) == 1) {
            return "";
        }
        $answer = '';
        //preg_match_all("/^([^【]|[^小][^题])[A-H]+/", $text, $qst);
        preg_match_all("/[A-H]+/", $text, $qst);

        foreach ($qst[0] as $k => $v) {
            if (preg_match("/^[A-H]/i", $v)/* in_array($v, ['A', 'B', 'C', 'D'] */) {
                $answer .= $v;
            }
        }

        foreach (array_count_values(str_split($answer, 1)) as $arr_v) {
            if ($arr_v > 1) {
                return "";
            }
        }

        return $answer;
    }

    /**
     * 根据题干内容判断是否是选择题
     * @param $content 题干内容
     * @return array|bool
     */
    private function isChoice($content)
    {
        $regs = ['optionsTable', 'A.', 'A'];

        foreach ($regs as $reg) {
            $idx = mb_strpos($content, $reg);
            if ($idx !== false) {
                if ($reg === 'optionsTable') {
                    $count = mb_substr_count($content, 'optionsTable');
                    if ($count > 1) {
                        return false; //可能是复合选择题
                    }
                }
                return [
                    'reg' => $reg,
                    'idx' => $idx
                ];
            }
        }
        return false;
    }

    /**
     * 提取题干中的选项：入口
     * @param $content      题干内容
     * @param $answer       格式化后的答案
     * @return bool         是否处理成功
     */
    private function handlerOption($content, $answer)
    {
        $choicePos = $this->isChoice($content);
        if ($choicePos === false) {
            return [];
        }

        $idx = $choicePos['idx'];
        if ($choicePos['reg'] === 'optionsTable') {
            return $this->optionsTableParse($content, $answer, $idx + 12);
        } else {
            return $this->abcdParse($content, $answer, $idx);
        }
    }

    //-------------------------------- 标准表格格式处理 class=optionsTable -------------------------------------

    /**
     * 处理 optionsTable class样式的表格
     * @param $content
     * @param $answer
     * @param $startIdx
     * @return array
     */
    private function optionsTableParse($content, $answer, $startIdx)
    {
        $options = [];
        $optionChars = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
        foreach ($optionChars as $index => $value) {
            $pos = $this->optionsTableOptionParse($content, $value, $startIdx);
            if ($pos === false)
                break;
            $startIdx = $pos['idx'];

            $isRight = mb_strpos($answer, $value) === false ? 0 : 1;
            array_push($options, [
                'option_id' => $index + 1,
                'option_trunk' => trim($pos['text']),
                'is_right' => $isRight,
            ]);
        }
        return $options;
    }

    /**
     * 提取 optionsTable class样式的表格中单个选项的内容
     * @param $content
     * @param $startChar
     * @param $idx
     * @return array|bool
     */
    private function optionsTableOptionParse($content, $startChar, $idx)
    {
        $startIdx = mb_strpos($content, $startChar, $idx);
        if ($startIdx === false)
            return false;

        $startCharLength = mb_strlen($startChar);
        $startIdx = $startIdx + $startCharLength;

        //取下一个不为空的字符串，如果是“.”则$startIdx 改为此位置
        $nextChar = $this->getNextNotEmptyStr($content, $startIdx);
        if ($nextChar === false)
            return false;
        if ($nextChar['text'] === '.' || $nextChar['text'] === '．') {
            $startIdx = $nextChar['idx'];
        }

        $endIdx = mb_strpos($content, '</td>', $startIdx);
        if ($endIdx === false) {
            $endIdx = mb_strlen($content) - 1;
            $nextIdx = $endIdx;
        } else {
            $nextIdx = $endIdx + 5;
        }
        $length = $endIdx - $startIdx;

        $text = mb_substr($content, $startIdx, $length);
        $text = str_replace(['&nbsp;', '<br />', '</br>', '<br>'], '', $text);

        return [
            'text' => $text,
            'idx' => $nextIdx
        ];
    }

    //-------------------------------- 非表格格式处理 -------------------------------------
    //使用正则方式处理，处理有问题，暂不使用
    private function abcdParse2($content, $answer, $idx)
    {
        $content .= 'theend';
        $isMach = preg_match_all('/[A|B|C|D|E|F|G](.*?)[B|C|D|E|br|theend]/s', $content, $match);
        if (!$isMach) {
            return '';
        }

        $options = [];
        foreach ($match[0] as $index => $value) {
            $text = mb_substr($value, 1);
            $optionKey = mb_substr($value, 0, 1);
            $isRight = mb_strpos($answer, $optionKey) === false ? 0 : 1;
            array_push($options, [
                'id' => $index + 1,
                'c' => trim($text),
                'aw' => $isRight,
            ]);
        }
        return json_encode($options, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 提取无表格形式的选项
     * @param $content
     * @param $answer
     * @param $idx
     * @return bool|string
     */
    private function abcdParse($content, $answer, $idx)
    {
        $options = [];
        $optionChars = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
        foreach ($optionChars as $index => $value) {
            $startIdx = mb_strpos($content, $value, $idx);
            if ($startIdx === false)
                break;
            if ($index === count($optionChars) - 1) {
                $text = mb_substr($content, $startIdx + 1);
            } else {
                $endIdx = mb_strpos($content, $optionChars[$index + 1], $startIdx);
                if ($endIdx === false) {
                    $endIdx = mb_strlen($content);
                }

                $text = mb_substr($content, $startIdx + 1, $endIdx - $startIdx - 1);
            }

            $nextChar = $this->getNextNotEmptyStr($text, 0);
            if ($nextChar === false)
                return false;
            if ($nextChar['text'] === '.' || $nextChar['text'] === '．') {
                $startIdx = $nextChar['idx'];
            }
            $text = mb_substr($text, $startIdx);
            $text = str_replace(['&nbsp;', '<br />', '</br>', '<br>'], '', $text);
            $isRight = mb_strpos($answer, $value) === false ? 0 : 1;
            array_push($options, [
                'option_id' => $index + 1,
                'option_trunk' => trim($text),
                'is_right' => $isRight,
            ]);
        }
        return $options;
    }

    //-----------------------------------工具函数------------------------------------

    /**
     * 取到首个不为空的字符
     * @param $str
     * @param $idx
     * @return array|bool  text首个字符的内容， idx 位置
     */
    private function getNextNotEmptyStr($str, $idx)
    {
        $len = mb_strlen($str, "UTF-8");
        for ($i = 0; $i < $len; $i++) {
            $ret = mb_substr($str, $idx + $i, 1, "UTF-8");
            if ($ret === ' ')
                continue;
            return [
                'text' => $ret,
                'idx' => $idx + $i + 1
            ];
        }
        return false;
    }

    /**
     * 字符串半角和全角间相互转换
     * @param string $str 待转换的字符串
     * @param string $type TODBC:转换为半角；TOSBC，转换为全角
     * @return string 返回转换后的字符串
     */
    private function convertStrType($str, $type = 'TOSBC')
    {

        $dbc = array(
            '０', '１', '２', '３', '４',
            '５', '６', '７', '８', '９',
            'Ａ', 'Ｂ', 'Ｃ', 'Ｄ', 'Ｅ',
            'Ｆ', 'Ｇ', 'Ｈ', 'Ｉ', 'Ｊ',
            'Ｋ', 'Ｌ', 'Ｍ', 'Ｎ', 'Ｏ',
            'Ｐ', 'Ｑ', 'Ｒ', 'Ｓ', 'Ｔ',
            'Ｕ', 'Ｖ', 'Ｗ', 'Ｘ', 'Ｙ',
            'Ｚ', 'ａ', 'ｂ', 'ｃ', 'ｄ',
            'ｅ', 'ｆ', 'ｇ', 'ｈ', 'ｉ',
            'ｊ', 'ｋ', 'ｌ', 'ｍ', 'ｎ',
            'ｏ', 'ｐ', 'ｑ', 'ｒ', 'ｓ',
            'ｔ', 'ｕ', 'ｖ', 'ｗ', 'ｘ',
            'ｙ', 'ｚ', '－', '　', '：',
            '．', '，', '／', '％', '＃',
            '！', '＠', '＆', '（', '）',
            '＜', '＞', '＂', '＇', '？',
            '［', '］', '｛', '｝', '＼',
            '｜', '＋', '＝', '＿', '＾',
            '￥', '￣', '｀'
        );

        $sbc = array(//半角
            '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E',
            'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O',
            'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y',
            'Z', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i',
            'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's',
            't', 'u', 'v', 'w', 'x',
            'y', 'z', '-', ' ', ':',
            '.', ',', '/', '%', ' #',
            '!', '@', '&', '(', ')',
            '<', '>', '"', '\'', '?',
            '[', ']', '{', '}', '\\',
            '|', '+', '=', '_', '^',
            '￥', '~', '`'
        );
        if ($type == 'TODBC') {
            return str_replace($sbc, $dbc, $str); //半角到全角
        } elseif ($type == 'TOSBC') {
            return str_replace($dbc, $sbc, $str); //全角到半角
        } else {
            return $str;
        }
    }

}
